package com.lht.admin.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.lht.admin.constants.DataValidConstant;
import com.lht.admin.entity.*;
import com.lht.admin.service.*;
import com.lht.admin.utils.ListUtil;
import com.lht.admin.utils.R;
import com.lht.admin.utils.RandomUtils;
import com.lht.admin.utils.StringUtils;
import com.lht.admin.vo.OptionsVO;
import com.lht.admin.vo.UploadFileResultVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * <p>
 *
 * @author cs12110 create at 2020-01-01 19:19
 * <p>
 * @since 1.0.0
 */
@Slf4j
@Service("commonService")
public class CommonValueServiceImpl implements CommonService {

    @Resource
    private ResideTypeService resideTypeService;
    @Resource
    private TaxLevelService taxLevelService;
    @Resource
    private LoanPurposeService loanPurposeService;
    @Resource
    private CompanyTypeService companyTypeService;
    @Resource
    private OverdueService overdueService;
    @Resource
    private SysUserService sysUserService;
    @Resource
    private IndustryTypeService industryTypeService;
    @Resource
    private LoanBankService loanBankService;
    @Resource
    private AutomobileBrandService automobileBrandService;
    @Resource
    private AddressProvinceService addressProvinceService;
    @Resource
    private AddressCityService addressCityService;
    @Resource
    private AddressAreaService addressAreaService;
    @Resource
    private BusinessPlaceService businessPlaceService;
    @Resource
    private ProductService productService;


    @Override
    public OptionsVO getOptionValues(SysUserEntity operator) {
        long start = System.currentTimeMillis();

        Map<String, Object> params = new HashMap<>(3);
        params.put("isDelete", DataValidConstant.IS_DELETE_FALSE_STR);

        OptionsVO optionsVO = new OptionsVO();
        try {
            optionsVO.setLoanBankList(getLoanBankOptions(params));
            optionsVO.setAutomobileBrandList(getAutomobileBrandOptions(params));
            optionsVO.setAgentList(getAgentOptions(operator, params));
            optionsVO.setIndustryTypeList(getIndustryTypeOptions(params));
            optionsVO.setResideTypeList(getResideTypeOptions(params));
            optionsVO.setTaxLevelList(getTaxLevelOptions(params));
            optionsVO.setLoanPurposeList(getLoanPurposeOptions(params));
            optionsVO.setCompanyTypeList(getCompanyTypeValues(params));
            optionsVO.setOverdueTypeList(getOverdueTypeList(params));
            optionsVO.setBusinessPlaceList(getBusinessPlaceOptions(params));
            optionsVO.setProductList(getProductOptions(params));

        } catch (Exception e) {
            log.error("Function[getOptionValues]", e);
        }

        long end = System.currentTimeMillis();

        log.info("Function[getOptionsValue] spend:{} mills", (end - start));

        return optionsVO;
    }


    private List<OptionsVO.OptionVO> getBusinessPlaceOptions(Map<String, Object> params) {
        List<BusinessPlaceEntity> list = businessPlaceService.queryAll(params);
        return doBlackMagic(list, (e) -> buildOption(e.getId(), e.getTitle(), e.getIsRent()));
    }

    private List<OptionsVO.OptionVO> getLoanBankOptions(Map<String, Object> params) {
        List<LoanBankEntity> list = loanBankService.queryAll(params);
        return doBlackMagic(list, (e) -> buildOption(e.getId(), e.getTitle()));
    }
    private List<OptionsVO.OptionVO> getAutomobileBrandOptions(Map<String, Object> params) {
        List<AutomobileBrandEntity> list = automobileBrandService.queryAll(params);
        return doBlackMagic(list, (e) -> buildOption(e.getId(), e.getTitle()));
    }

    private List<OptionsVO.OptionVO> getIndustryTypeOptions(Map<String, Object> params) {
        List<IndustryTypeEntity> list = industryTypeService.queryAll(params);
        return doBlackMagic(list, (e) -> buildOption(e.getId(), e.getTitle()));
    }

    private List<OptionsVO.OptionVO> getAgentOptions(SysUserEntity operator, Map<String, Object> params) {
        params.put("dept_id", operator.getDeptId());
        params.put("status", 1);

        List<SysUserEntity> list = sysUserService.queryList(params);
        return doBlackMagic(list, (e) -> buildOption(e.getUserId(), e.getUsername()));
    }

    private List<OptionsVO.OptionVO> getResideTypeOptions(Map<String, Object> params) {
        List<ResideTypeEntity> list = resideTypeService.queryAll(params);
        return doBlackMagic(list, (e) -> buildOption(e.getId(), e.getTitle()));
    }


    private List<OptionsVO.OptionVO> getTaxLevelOptions(Map<String, Object> params) {
        List<TaxLevelEntity> list = taxLevelService.queryAll(params);
        return doBlackMagic(list, (e) -> buildOption(e.getId(), e.getLevel()));
    }


    private List<OptionsVO.OptionVO> getLoanPurposeOptions(Map<String, Object> params) {
        List<LoanPurposeEntity> list = loanPurposeService.queryAll(params);
        return doBlackMagic(list, (e) -> buildOption("" + e.getId(), e.getTitle(), e.getIsInput()));
    }


    private List<OptionsVO.OptionVO> getCompanyTypeValues(Map<String, Object> params) {
        List<CompanyTypeEntity> list = companyTypeService.queryAll(params);
        return doBlackMagic(list, (e) -> buildOption(e.getId(), e.getTitle()));
    }


    private List<OptionsVO.OptionVO> getOverdueTypeList(Map<String, Object> params) {
        List<OverdueEntity> list = overdueService.queryAll(params);
        return doBlackMagic(list, (e) -> buildOption(e.getId(), e.getTitle()));
    }
    private List<OptionsVO.OptionVO> getProductOptions(Map<String, Object> params) {
        List<ProductEntity> list = productService.queryAll(params);
        return doBlackMagic(list, (e) -> buildOption(e.getId(), e.getTitle()));
    }


    @Override
    public List<OptionsVO.OptionVO> getProvince() {

        QueryWrapper<AddressProvinceEntity> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("is_delete", DataValidConstant.IS_DELETE_FALSE_STR);

        List<AddressProvinceEntity> provinceList = addressProvinceService.list(queryWrapper);
        if (ListUtil.isEmpty(provinceList)) {
            return Collections.emptyList();
        }

        return provinceList
                .stream()
                .map(e -> buildOption(e.getCode(), e.getName()))
                .collect(Collectors.toList());
    }


    @Override
    public List<OptionsVO.OptionVO> getCity(String provinceCode) {
        QueryWrapper<AddressCityEntity> queryWrapper = new QueryWrapper<>();
        queryWrapper
                .eq("is_delete", DataValidConstant.IS_DELETE_FALSE_STR)
                .eq("province_code", provinceCode);


        List<AddressCityEntity> list = addressCityService.list(queryWrapper);
        return doBlackMagic(list, (e) -> buildOption(e.getCode(), e.getName()));
    }

    @Override
    public List<OptionsVO.OptionVO> getArea(String cityCode) {
        QueryWrapper<AddressAreaEntity> queryWrapper = new QueryWrapper<>();
        queryWrapper
                .eq("is_delete", DataValidConstant.IS_DELETE_FALSE_STR)
                .eq("city_code", cityCode);


        List<AddressAreaEntity> list = addressAreaService.list(queryWrapper);
        return doBlackMagic(list, (e) -> buildOption(e.getCode(), e.getName()));
    }


    /**
     * do black magic
     *
     * @param originList origin list
     * @param function   parse function
     * @param <T>        type of origin list
     * @param <V>        type of parse list
     * @return List of V
     */
    private <T, V> List<V> doBlackMagic(List<T> originList, Function<T, V> function) {
        if (ListUtil.isEmpty(originList)) {
            return Collections.emptyList();
        }

        return originList
                .stream()
                .map(function)
                .collect(Collectors.toList());
    }

    private OptionsVO.OptionVO buildOption(Object value, String label) {
        OptionsVO.OptionVO optionVO = new OptionsVO.OptionVO();
        optionVO.setValue(value);
        optionVO.setLabel(label);

        return optionVO;
    }

    private OptionsVO.OptionVO buildOption(Object value, String label, String other) {
        OptionsVO.OptionVO optionVO = new OptionsVO.OptionVO();
        optionVO.setValue(value);
        optionVO.setLabel(label);
        optionVO.setOther(other);

        return optionVO;
    }


    @Override
    public void download(HttpServletResponse response, String fileName) {
        log.info("Function[download] file:{}", fileName);
        try (ServletOutputStream stream = response.getOutputStream()) {
            // 设置头部文件和文件路名中文
            response.setHeader("content-type", "application/octet-stream");
            response.setContentType("application/octet-stream");
            response.setHeader("Content-Disposition", "attachment;filename=" + java.net.URLEncoder.encode(fileName, "UTF-8"));

            int len;
            byte[] arr = new byte[2048];
            String path = handleFileStoragePath(fileName);
            FileInputStream in = new FileInputStream(new File(path));

            while (-1 != (len = in.read(arr))) {
                stream.write(arr, 0, len);
            }

            stream.flush();
        } catch (Exception e) {
            log.error("download,file:" + fileName, e);
        }
    }

    @Override
    public UploadFileResultVO uploadFile(MultipartFile file) {
        UploadFileResultVO resultVO = new UploadFileResultVO();
        resultVO.setStatus(R.StatusEnum.FAILURE.getValue());
        if (file == null) {
            resultVO.setMessage("file is null");
            return resultVO;
        }

        try {
            String fileName = file.getOriginalFilename();
            String saveName = buildFileNameWithTimestamp(fileName);

            log.info("Function[uploadFile] origin file name:{},save as:{} ", fileName, saveName);


            String storagePath = handleFileStoragePath(saveName);
            FileOutputStream out = new FileOutputStream(storagePath);

            out.write(file.getBytes());
            out.flush();
            out.close();

            resultVO.setStatus(R.StatusEnum.SUCCESS.getValue());
            resultVO.setUrl(storagePath);
        } catch (Exception e) {
            log.error("Function[uploadFile]", e);
        }

        return resultVO;
    }


    private String buildFileNameWithTimestamp(String fileName) {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
        String format = sdf.format(new Date());
        String uuid = RandomUtils.createUUID();

        return uuid.substring(0, 8) + format + getSuffix(fileName);
    }

    private String getSuffix(String fileName) {
        if (StringUtils.isEmpty(fileName)) {
            return "";
        }

        int last = fileName.lastIndexOf(".");
        if (last == -1) {
            return "";
        }

        return fileName.substring(last).toLowerCase();
    }

    /**
     * 获取文件存储路径
     *
     * @param fileName 文件名称
     * @return String
     */
    private String handleFileStoragePath(String fileName) {
        return "" + fileName;
    }
}
